How To Auto Fit Text To An Image With PHP and WordPress

Recently I took on a project where a client needed to generate an image on the fly, with a particular quote and they wanted to scale the text to fit the image on the fly. Which sounded perfectly daunting.

But, I decided to try to tackle the problem anyway.

It turns out there are plenty of tutorials on how to add text to an image with PHP using imagettftext, or Imagick and annotateImage.  But none of them deal with the specific problem of scaling your text to fit in a particular space on the image.  Nor do they adequately handle fonts and sizing and text wrapping.

In my searches, I came upon a few pieces that helped me get there, and ultimately resulted in a magic function I’m calling: autofit_text_to_image()

Autofitting Text To An Image With PHP

Enough talk. Here’s the guts of the new function:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
<?php
/**
* Auto Fit Text To Image
*
* Write text to image using a target width, height, and starting font size.
*
* @author Clif Griffin
* @url http://cgd.io/2014/auto-fit-text-to-an-image-with-php-and-wordpress
*
* @access public
* @param bool $canvas_image_filename (default: false) The base image.
* @param string $dest_filename (default: 'output.jpg') The output filename.
* @param string $text (default: '') The text being written.
* @param int $starting_font_size (default: 60) The starting (max) font size.
* @param int $max_height (default: 500) The maximum height in lines of text.
* @param int $x_pos (default: 0) X position in pixels for first line of text.
* @param int $y_pos (default: 0) Y position in pixels for first line of text.
* @param bool $font_file (default: false) Path to font file (.ttf)
* @param string $font_color (default: 'black') The color. Also accepts rgba values. Example: "rgba(0,0,0,0.5)"
* @param int $line_height_ratio (default: 1) Allows scaling the line height. Should be between 0.1 and 1.
* @param string $dest_format (default: 'jpg') Output format.
* @return bool True: success, False: failure
*/
function autofit_text_to_image( $canvas_image_filename = false, $dest_filename = 'output.jpg', $text = '', $starting_font_size = 60, $max_width = 500, $max_height = 500, $x_pos = 0, $y_pos = 0, $font_file = false, $font_color = 'black', $line_height_ratio = 1, $dest_format = 'jpg' ) {
// Bail if any essential parameters are missing
if ( ! $canvas_image_filename || ! $dest_filename || empty($text) || ! $font_file || empty($font_color) || empty($max_width) || empty($max_height) ) return false;
// Do we have a valid canvas image?
if ( ! file_exists($canvas_image_filename) ) return;
$canvas_handle = fopen( $canvas_image_filename, 'rb' );
// Load image into Imagick
$NewImage = new Imagick();
$NewImage->readImageFile($canvas_handle);
// Instantiate Imagick utility objects
$draw = new ImagickDraw();
$pixel = new ImagickPixel( $font_color );
// Load Font
$font_size = $starting_font_size;
$draw->setFont($font_file);
$draw->setFontSize($font_size);
// Holds calculated height of lines with given font, font size
$total_height = 0;
// Run until we find a font size that doesn't exceed $max_height in pixels
while ( 0 == $total_height || $total_height > $max_height ) {
if ( $total_height > 0 ) $font_size--; // we're still over height, decrement font size and try again
$draw->setFontSize($font_size);
// Calculate number of lines / line height
// Props users Sarke / BMiner: http://stackoverflow.com/questions/5746537/how-can-i-wrap-text-using-imagick-in-php-so-that-it-is-drawn-as-multiline-text
$words = preg_split('%\s%', $text, -1, PREG_SPLIT_NO_EMPTY);
$lines = array();
$i = 0;
$line_height = 0;
while ( count($words) > 0 ) {
$metrics = $NewImage->queryFontMetrics( $draw, implode(' ', array_slice($words, 0, ++$i) ) );
$line_height = max( $metrics['textHeight'], $line_height );
if ( $metrics['textWidth'] > $max_width || count($words) < $i ) {
$lines[] = implode( ' ', array_slice($words, 0, --$i) );
$words = array_slice( $words, $i );
$i = 0;
}
}
$total_height = count($lines) * $line_height * $line_height_ratio;
if ( $total_height === 0 ) return false; // don't run endlessly if something goes wrong
}
// Writes text to image
for( $i = 0; $i < count($lines); $i++ ) {
$NewImage->annotateImage( $draw, $x_pos, $y_pos + ($i * $line_height * $line_height_ratio), 0, $lines[$i] );
}
$NewImage->setImageFormat($dest_format);
$result = $NewImage->writeImage($dest_filename);
return $result;
}

Now, I know: that looks terrifying.  But it isn’t so bad.  It just turns out there are quite a few things we need to know to at the outset.

Let’s walk through an example.  For this demo, we will simply need an image to write text to and a font. Here are my files:

Auto fit text to image using PHP file structure

My file structure.

quote_canvas.png is the image I’m going to write text to. Cedarville-Cursive.ttf is my font.

And here is test.php:

1 2 3 4 5 6 7 8 9 10
<?php
// autofit_text_to_image() example
// http://cgd.io/2014/auto-fit-text-to-an-image-with-php-and-wordpress
$canvas_image_filename = 'quote_canvas.png';
$dest_filename = 'quote.jpg';
$text = 'I believe in pink. I believe that laughing is the best calorie burner. I believe in kissing, kissing a lot.';
$font_file = 'Cedarville-Cursive.ttf';
autofit_text_to_image($canvas_image_filename, $dest_filename, $text, 60, 500, 400, 40, 400, $font_file);
view raw gistfile1.php hosted with ❤ by GitHub

When I run test.php, a new file appears in the directory:
Auto fit text to image using PHP file structure after file new image generated.

Which contains my original image, plus the text we wrote to it:
Auto fitted text on image using PHP

Now, what happens if we reduce the length of our text and run it again?  Here’s the output:

Larger auto fitted text on image using PHP

You’ll notice the font size increased to the max starting font size we specified in the beginning: 60

If we conversely increase the amount of text, the font size will decrease:

Smaller auto fitted text on image using PHP

Putting it Together with WordPress

What if we were in a WordPress context and wanted to create the image, put it in the proper WordPress uploads directory and then set the current post’s featured image to our newly generated image?  It would look something like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
<?php
// autofit_text_to_image() WordPress example
// http://cgd.io/2014/auto-fit-text-to-an-image-with-php-and-wordpress
$canvas_image_filename = 'quote_canvas.png';
$dest_filename = trailingslashit($upload_dir['path']) . "featured_image_{$post->ID}.jpg";
$text = 'I believe in pink. I believe that laughing is the best calorie burner. I believe in kissing, kissing a lot.';
$font_file = 'Cedarville-Cursive.ttf';
$result = autofit_text_to_image($canvas_image_filename, $dest_filename, $text, 60, 500, 400, 40, 400, $font_file);
if ($result) {
$upload_dir = wp_upload_dir();
$filetype = wp_check_filetype( basename( $dest_filename ), null );
$attachment = array(
'guid' => $upload_dir['url'] . '/' . basename( $dest_filename ),
'post_mime_type' => $filetype['type'],
'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $dest_filename ) ),
'post_content' => '',
'post_status' => 'inherit'
);
$new_attachment = wp_insert_attachment( $attachment, $dest_filename, $post->ID );
$attach_data = wp_generate_attachment_metadata( $new_attachment, $dest_filename );
wp_update_attachment_metadata( $new_attachment, $attach_data );
update_post_meta($post->ID, '_thumbnail_id', $new_attachment);
}
view raw gistfile1.php hosted with ❤ by GitHub

The above code could be easily implemented within an action on save_post, and could even load the text details from the post attributes or meta.

A Word on Performance

This function should not be run on the fly.  You should only use it to generate and save an image.

Calculating the line heights and correct font size is an expensive operation and it would be quite easy for you to take down your site, improperly implemented.

Comments

  1. hema says

    I’m interested in this code,
    so I understand that i put the function code in function.php of wordpress or function.php of theme?
    then the php code where to put ?if i want to use to make text image from my post titles?
    thanks

      • hema says

        so why you don’t make it a plugin free or paid, and the idea not found before,
        i always search for the idea of writing text on image for my blog and nothing found on internet except you!!
        so make it a plugin , and grow your business

  2. says

    Seems great – will try this out.

    I have been searching the web for a solution like this and your code seems to be the most mature example to find.

    To let you have part on my idea:

    I would like to take some text that I will have to write into a meta field of my post, generate a quote-thumbnail from it and but it in the meta tags.
    So if my article will be shared on facebook there will be an image with an amazing quote – so people might share it more easily

Leave a Reply